using FireStarter.Base;
using FireStarter.Base.BusinessObjectMerger;
using Firestarter.DB.Schema.BO;
using Firestarter.DB.Schema.Logic;
using Firestarter.DB.Schema.Test.BO.TestUtils;
using Habanero.BO;
using Habanero.BO.ClassDefinition;
using Habanero.BO.Loaders;
using Habanero.Testability;
using NUnit.Framework;

namespace Firestarter.DB.Schema.Test.Logic
{
    [TestFixture]
    public class TestMergeDBPrimaryKey
    {
        [TestFixtureSetUp]
        public virtual void TestFixtureSetup()
        {
            ClassDef.ClassDefs.Clear();
            string xmlClassDefs = BOBroker.GetClassDefsXml();
            ClassDef.ClassDefs.Add(new XmlClassDefsLoader(xmlClassDefs, new DtdLoader()).LoadClassDefs());
            BORegistry.DataAccessor = new DataAccessorInMemory();

            MergeableBORegistry.Instance.Register<DBPrimaryKey, DBMergeDBPrimaryKey>();
            MergeableBORegistry.Instance.Register<DBPrimaryKeyColumn, DBMergeDBPrimaryKeyColumn>();
        }

        [SetUp]
        public virtual void SetupTest()
        {
        }

        [TestCase("PrimaryKeyID", false)]
        [TestCase("TableID", false)]
        [TestCase("Approved", false)]
        [TestCase("PendingChangeType", false)]
        public void Test_MustMergeProp_ShouldBeSetForProp(string propName, bool expectedMustMergeProp)
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBPrimaryKey> merger = new DBMergeBO<DBPrimaryKey>();
            var dbPrimaryKey = new DBPrimaryKey();
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            var boProp = dbPrimaryKey.Props[propName];
            var returnedMustMergeProp = merger.MustMergeProperty(boProp);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedMustMergeProp, returnedMustMergeProp, "Prop '" + propName + "' should be MustMerge = '" + expectedMustMergeProp +"'");
        }

        [TestCase("PrimaryKeyColumns", true)]
        [TestCase("MappedDMObjectIdentity", false)]
        [TestCase("Table", false)]
        public void Test_MustMergeRelationship_ShouleBeSetForRelationships(string relationshipName, bool expectedMustMergeRel)
        {
            //---------------Set up test pack-------------------
            DBMergeBO<DBPrimaryKey> merger = new DBMergeBO<DBPrimaryKey>();
            var dbPrimaryKey = new DBPrimaryKey();
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------

            var relationship = dbPrimaryKey.Relationships[relationshipName];
            var returnedMustMergeProp = merger.MustMergeRelationship(relationship);
            //---------------Test Result -----------------------
            Assert.AreEqual(expectedMustMergeRel, returnedMustMergeProp, "Relationship '" + relationship.RelationshipName + "' should be MustMerge = '" + expectedMustMergeRel + "'");
        }

        [Test]
        public void Test_MatchBusinessObject_WhenTableNameEquals_ShouldBeTrue()
        {
            //---------------Set up test pack-------------------
            DBPrimaryKey fromPK = TestUtilsDBPrimaryKey.GetTestFactory()
                .WithOne<DBPrimaryKeyColumn>(key => key.PrimaryKeyColumns)
                .CreateValidBusinessObject();
            DBPrimaryKey toPK = new DBPrimaryKey();
            toPK.Table = new DBTable(fromPK.Table.TableName);

            var pkMerger = (DBMergeBO<DBPrimaryKey>)MergeableBORegistry.Instance.Resolve<DBPrimaryKey>();
            //---------------Assert Precondition----------------
            Assert.AreEqual(fromPK.Table.TableName, toPK.Table.TableName);
            Assert.AreNotSame(fromPK.Table, toPK.Table);
            Assert.AreNotEqual(fromPK.ToString(), toPK.ToString());
            //---------------Execute Test ----------------------
            var function = pkMerger.MatchBusinessObject(fromPK);
            var matchRelationship = function(toPK);
            //---------------Test Result -----------------------
            Assert.IsTrue(matchRelationship);
        }
        [Test]
        public void Test_MatchBusinessObject_WhenPKNameNotEqual_ShouldBeFalse()
        {
            //---------------Set up test pack-------------------
            DBPrimaryKey fromPK = TestUtilsDBPrimaryKey.GetTestFactory()
                .WithOne(key => key.PrimaryKeyColumns)
                .CreateValidBusinessObject();
            DBPrimaryKey toPK = new DBPrimaryKey();
            toPK.Table = new DBTable(GetRandomString());

            DBMergeBO<DBPrimaryKey> pkMerger = (DBMergeBO<DBPrimaryKey>)MergeableBORegistry.Instance.Resolve<DBPrimaryKey>();
            //---------------Assert Precondition----------------
            Assert.AreNotEqual(fromPK.TableName, toPK.TableName);
            //---------------Execute Test ----------------------
            var function = pkMerger.MatchBusinessObject(fromPK);
            var matchRelationship = function(toPK);
            //---------------Test Result -----------------------
            Assert.IsFalse(matchRelationship);
        }

        [Test]
        public void Test_MergePK_WithAddedPKColumn_ShouldCreateMergedPKColumn_AndCreateColumns()
        {
            //---------------Set up test pack-------------------
            var fromPK = CreatePK();
            var toPK = GetPKMerger().MergeBOFrom(fromPK, null);
            toPK.Table = new DBTable();
            var fromPKColumn = CreatePKColumn(fromPK);

            //---------------Assert Precondition---------------
            Assert.AreEqual(1, fromPK.PrimaryKeyColumns.Count);
            Assert.IsNotNull(fromPKColumn.Column);
            Assert.IsNotNull(fromPK.Table);
            Assert.AreEqual(0, toPK.PrimaryKeyColumns.Count);
            Assert.IsNotNull(toPK.Table);
            //---------------Execute Test ----------------------
            var merger = GetPKMerger();
            merger.MergeBOFrom(fromPK, toPK);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, toPK.PrimaryKeyColumns.Count);
            var createdPKColumn = toPK.PrimaryKeyColumns[0];

            Assert.AreEqual(fromPKColumn.ColumnName, createdPKColumn.ColumnName, "Column should have been linked to the pkColumn");
            Assert.AreEqual(ChangeType.Add, createdPKColumn.Column.PendingChangeType);
        }

        [Test]
        public void Test_MergePK_WithAddedPKColumn_ShouldAddCreatedColumnsToParentAndRelatedTables()
        {
            //---------------Set up test pack-------------------
            var fromPK = CreatePK();
            var toPK = MergeableBORegistry.Instance.Resolve<DBPrimaryKey>().MergeBOFrom(fromPK, null);
            toPK.Table = new DBTable();
            var fromPKColumn = CreatePKColumn(fromPK);

            //---------------Assert Precondition---------------
            Assert.AreEqual(1, fromPK.PrimaryKeyColumns.Count);
            Assert.IsNotNull(fromPKColumn.Column);
            Assert.IsNotNull(fromPK.Table);

            Assert.AreEqual(0, toPK.PrimaryKeyColumns.Count);
            Assert.IsNotNull(toPK.Table);
            //---------------Execute Test ----------------------
            DBMergeBO<DBPrimaryKey> merger = (DBMergeBO<DBPrimaryKey>)MergeableBORegistry.Instance.Resolve<DBPrimaryKey>();
            merger.MergeBOFrom(fromPK, toPK);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, toPK.PrimaryKeyColumns.Count);
            var createdPKColumn = toPK.PrimaryKeyColumns[0];

            Assert.IsTrue(toPK.Table.Columns.Contains(createdPKColumn.Column), "The Column should have been added to the table");
        }

        private static IMergeBO<DBPrimaryKey> GetPKMerger()
        {
            return MergeableBORegistry.Instance.Resolve<DBPrimaryKey>();
        }

        private static string GetRandomString()
        {
            return RandomValueGen.GetRandomString();
        }

        private static DBPrimaryKey CreatePK()
        {
            var parentTable = TestUtilsDBTable.GetTestFactory()
                .WithValue(table => table.Database, null)
                .CreateValidBusinessObject();
            return CreatePK(parentTable);
        }

        private static DBPrimaryKey CreatePK(DBTable parentTable)
        {
            return TestUtilsDBPrimaryKey.GetTestFactory()
                .WithValue(relationship => relationship.Table, parentTable)
                .CreateValidBusinessObject();
        }

        private static DBPrimaryKeyColumn CreatePKColumn(DBPrimaryKey pk, DBColumn parentColumn)
        {
            return TestUtilsDBPrimaryKeyColumn.GetTestFactory()
                .WithValue(relProp => relProp.PrimaryKeyDB, pk)
                .WithValue(relProp => relProp.Column, parentColumn)
                .CreateValidBusinessObject();
        }
        private static DBPrimaryKeyColumn CreatePKColumn(DBPrimaryKey pk)
        {
            return CreatePKColumn(pk, GetRandomString());
        }

        private static DBPrimaryKeyColumn CreatePKColumn(DBPrimaryKey pk, string parentColumnName)
        {
            var parentColumn = TestUtilsDBColumn.GetTestFactory()
                .WithValue(column => column.ColumnName, parentColumnName)
                .WithValue(column => column.Table, pk.Table)
                .CreateValidBusinessObject();

            return CreatePKColumn(pk, parentColumn);
        }
    }
}